package com.wb.config

import com.wb.component.Slf4j.Companion.log
import com.wb.component.event.OnGroup
import com.wb.component.event.OnPrivate
import com.wb.entity.MsgGet
import com.wb.entity.RevokeMsg
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import java.io.File
import java.lang.reflect.Method
import java.net.JarURLConnection
import java.net.URLDecoder
import java.nio.charset.Charset


/**
 * 消息注解初始化配置
 *
 * @author 祁雪
 */
@Configuration
open class MessageFunConfig {

    companion object {
        val eventMap = mapOf<String, Class<out Annotation>>(
            "EventGroupMsg" to OnGroup::class.java,
            "EventFriendMsg" to OnPrivate::class.java
        )
        val eventMapMethod = hashMapOf<String, HashMap<Method, Class<*>>>()
    }

    @Bean
    open fun eventMsg() {

        val packageName = "com.wb.listening"
        log.info("开始初始化消息监听 package => ${packageName}")
        eventMap.forEach { k, v ->

            val list = classScan(
                packageName,
                v
            )
            list.forEach {
                it.methods.forEach { me ->
                    if (me.isAnnotationPresent(v)) {
//                        println("查询到 => ${me.name}")
                        if (eventMapMethod.containsKey(k) && !eventMapMethod.get(k)!!.containsKey(me)) {
                            eventMapMethod.get(k)!!.put(me, it)
                        } else {
                            eventMapMethod.put(
                                k, hashMapOf(
                                    me to it
                                )
                            )
                        }
                    }
                }
            }
        }

//        eventMapMethod.values.forEach {
//            it.keys.forEach {
//                println(it.name)
//            }
//        }

        log.info(
            "已查询到消息监听 共${eventMapMethod.size}类 => ${
                eventMapMethod.values.map {
                    it.values
                }.flatMap { it }.toList().size
            }个方法"
        )
    }


    /**
     * 包扫描
     * @param clazz 注解
     * @return 返回这个包下包含这个注解的类
     */
    private fun classScan(basePackage: String, clazz: Class<out Annotation>): ArrayList<Class<*>> {

        var basePackageStr = basePackage
        if (basePackageStr.endsWith(".")) {
            basePackageStr = basePackageStr.substring(0, basePackageStr.lastIndexOf("."))
        }

        val basePackagePath = basePackageStr.replace(".", "/")

        val resources = Thread.currentThread().contextClassLoader.getResources(basePackagePath)

        val list = arrayListOf<Class<*>>()
        while (resources.hasMoreElements()) {
            val resource = resources.nextElement()
//            println(resource.protocol)
            // 解析file
            if ("file".equals(resource.protocol)) {
                val filePath: String = URLDecoder.decode(resource.file, Charset.defaultCharset())
                val ts = doClassScan(filePath, basePackageStr, clazz)
                if (ts != null) {
                    list.addAll(ts)
                }
            } else if ("jar".equals(resource.protocol)) {
                val url = resource.openConnection() as JarURLConnection
                val jarFile = url.jarFile

                val jarEntrys = jarFile.entries()

                while (jarEntrys.hasMoreElements()) {
                    val jarEntry = jarEntrys.nextElement()
                    val jarEntryName = jarEntry.name

                    if (jarEntryName.contains(".class") && jarEntryName.replace("/", ".").startsWith(basePackage)) {
                        val className: String =
                            jarEntryName.substring(0, jarEntryName.lastIndexOf(".")).replace("/", ".")
                        val cls = Class.forName(className)
                        list.add(cls)
                    }
                }
            }
        }
        return list
    }

    private fun doClassScan(path: String, packageName: String, clazz: Class<out Annotation>): ArrayList<Class<*>>? {

        val file = File(path)

        val list = arrayListOf<Class<*>>()
        if (!file.exists()) {
            return null
        }

        if (file.isFile) {

            val className = file.name.substring(0, file.name.length - 6)
            try {

                val loadClass = Thread.currentThread().contextClassLoader.loadClass("${packageName}.${className}");

//                val any = loadClass.getDeclaredConstructor().newInstance()

                val flag = loadClass.methods.any {
                    it.isAnnotationPresent(clazz)
                }

                if (flag) {
                    list.add(loadClass)
                }

            } catch (e: ClassNotFoundException) {
                log.error("class ${packageName}.${className} 不存在")
            }

        } else {
            file.listFiles()?.forEach {
                val ts = doClassScan(it.absolutePath, packageName, clazz)
                if (ts != null) {
                    list.addAll(ts)
                }
            }
        }

        return list
    }

}